# -*- coding: utf-8 -*-
import hashlib
import time
import urllib
from collections import OrderedDict

import requests
import json

from django.conf import settings
from django.template import Context
from django.template.loader import get_template

from async import async_job
from common.cache import redis_cache
from common.channel.pay import check_channel_order
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.utils.ip_address import check_valid_ip_address

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    '20000004': {
        'API_KEY': '1WQXOLVEO7ZWJKERUXSC7GZ8IPALGTMNBVTLPAOCR'
                   'BIFX6WYDPTZMMQ4XBAWLHTXB4Z8AI7DGP7VT3RLSKVY'
                   'RVXUE3GOMQHW3UKUCCUEMUIVUJV8PPE0VAFDMWJZONAG',
        'order_gateway': 'http://27.155.88.76:3020/api/pay/create_order',
        'pc_gateway': 'http://27.155.88.76:3020/api/cashier/pc_build',
        'query_gateway': 'http://27.155.88.76:3020/api/pay/query_order',
        # 通道后台申请
        'appId': '7c4f2ac6752849c5bb426f15f8d4a99d',
    }
}


# 费率1.5 限额：50~5000
# 支付方式接口，service应该对应后台传入的变量名(service对应是channel的支付方式）
def _get_pay_type(service):
    # 开通的银联二维码
    if service == 'unionpay':
        return '8014'
    else:
        return '8014'


def _get_api_key(mch_id):
    return APP_CONF[mch_id]['API_KEY']


def _get_order_gateway(mch_id):
    return APP_CONF[mch_id]['order_gateway']


def _get_pc_gateway(mch_id):
    return APP_CONF[mch_id]['pc_gateway']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _get_app_id(mch_id):
    return APP_CONF[mch_id]['appId']


def verify_notify_sign(params, key):
    sign = params['sign']
    params.pop('sign')
    calculated_sign = _check_m_sign(params, key)
    if sign.upper() != calculated_sign.upper():
        _LOGGER.info("dezhunpay sign: %s, calculated sign: %s", sign, calculated_sign)
        raise ParamError('sign not pass, data: %s' % params)


# md5加密
def _md5_sign(s):
    m = hashlib.md5()
    m.update(s)
    sign = m.hexdigest().upper()
    return sign


# 去除值为空的，排序后并加密
def _check_m_sign(paramter, key):
    for k in paramter.keys():
        if paramter.get(k) == '':
            paramter.pop(k)
    paramter_str = ''
    for k in sorted(paramter.keys()):
        paramter_str += '%s=%s&' % (k, paramter[k])
    paramter_str += 'key=%s' % key
    return _md5_sign(paramter_str)


# 创建支付订单
def create_charge(pay, pay_amount, info):
    app_id = info['app_id']
    service = info.get('service')
    parameter_dict = OrderedDict((
        # 商户订单号,商户唯一
        ('mchId', int(app_id)),
        # 该商户创建的应用对应的ID，接口方管理后台自己添加的
        ('appId', _get_app_id(app_id)),
        # 支付产品ID
        ('productId', int(_get_pay_type(service))),
        # 商户生成的订单号
        ('mchOrderNo', str(pay.id)),
        # 三位货币代码,人民币:cny
        ('currency', 'cny'),
        # 支付金额,单位分
        ('amount', str(int(pay_amount * 100))),
        # 异步通知地址
        ('notifyUrl', '{}/pay/api/{}/dezhunpay/{}'.format(
            settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
        # 商品主题
        ('subject', 'none'),
        # 商品描述信息
        ('body', 'none'),
        # 附加参数
        ("extra", 'none'),
    ))
    parameter_dict['sign'] = _check_m_sign(parameter_dict, _get_api_key(app_id))
    parameter = urllib.urlencode(parameter_dict).encode('utf-8')
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_order_gateway(app_id), data=parameter, headers=headers, timeout=10)
    resp_obj = json.loads(response.text)
    _LOGGER.info("dezhunpay create: %s,%s mchOrderNo is: %s", json.dumps(parameter_dict), resp_obj,
                 parameter_dict['mchOrderNo'])
    if resp_obj['retCode'] == 'SUCCESS':
        codeImgUrl = resp_obj['payParams']['codeImgUrl']
        template_data = {'base64_img': codeImgUrl, 'amount': pay_amount, 'due_time': int(time.time()) + 180,
                         'order_id': parameter_dict['mchOrderNo'], }
        t = get_template('cloud_flash_dezhunpay.html')
        html = t.render(Context(template_data))
        cache_id = redis_cache.save_html(pay.id, html)
        _LOGGER.info("dezhunpay create_url: %s, pay.id: %s", settings.PAY_CACHE_URL + cache_id, pay.id)
        return {'charge_info': settings.PAY_CACHE_URL + cache_id}
    else:
        response = requests.post(_get_pc_gateway(app_id), data=parameter, headers=headers, timeout=10)
        resp_obj = json.loads(response.text)
        if resp_obj['retCode'] == 'SUCCESS':
            codeImgUrl = resp_obj['payParams']['codeImgUrl']
            template_data = {'base64_img': codeImgUrl, 'amount': pay_amount, 'due_time': int(time.time()) + 180,
                             'order_id': parameter_dict['mchOrderNo'], }
            t = get_template('cloud_flash_dezhunpay.html')
            html = t.render(Context(template_data))
            cache_id = redis_cache.save_html(pay.id, html)
            _LOGGER.info("dezhunpay create_url: %s, pay.id: %s", settings.PAY_CACHE_URL + cache_id, pay.id)
            return {'charge_info': settings.PAY_CACHE_URL + cache_id}
        return {'charge_info': resp_obj}


# OK
def check_notify_sign(request, app_id):
    api_key = _get_api_key(app_id)
    data = dict(request.POST.iteritems())
    _LOGGER.info("dezhunpay notify data: %s", data)
    _LOGGER.info("dezhunpay notify data: %s, mchOrderNo is: %s", data, data['mchOrderNo'])
    verify_notify_sign(data, api_key)
    pay_id = data['mchOrderNo']
    check_valid_ip_address(str(request.META['REMOTE_ADDR']), pay_id)
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s", data)
        raise ParamError('dezhunpay event does not contain pay ID')
    pay = order_db.get_pay(int(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id  # 商户编号
    trade_status = int(data['status'])
    trade_no = data['payOrderId']
    total_fee = float(data['amount']) / 100.0
    extend = {
        'status': trade_status,
        'trade_no': trade_no,
        'amount': total_fee,
    }

    check_channel_order(pay_id, total_fee, app_id)
    if trade_status == 2:
        _LOGGER.info('dezhunpay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    """ 查询订单 """
    pay_id = pay_order.id
    api_key = _get_api_key(app_id)
    dct = OrderedDict((
        # 商户订单号,商户唯一
        ('mchId', app_id),
        # 该商户创建的应用对应的ID
        ('appId', _get_app_id(app_id)),
        # 商户生成的订单号
        ('mchOrderNo', pay_id),
    ))
    dct['sign'] = _check_m_sign(dct, api_key)
    _LOGGER.info('dezhunpay query data: %s, order_id is: %s', json.dumps(dct), dct['mchOrderNo'])
    response = requests.post(url=_get_query_gateway(app_id), data=dct, timeout=10)
    _LOGGER.info('dezhunpay query rsp: %s', response.text)
    data = json.loads(response.text)
    if response.status_code == 200:
        status = data['retCode']
        if status == 'SUCCESS':
            trade_status = str(data['status'])
            total_fee = float(data['amount']) / 100.0
            trade_no = str(data['mchOrderNo'])
            extend = {
                'trade_status': trade_status,
                'trade_no': trade_no,
                'total_fee': total_fee
            }
            if trade_status == '2' or trade_status == '3':
                _LOGGER.info('dezhunpay query order success, mch_id:%s pay_id:%s' % (pay_order.mch_id, pay_order.id))
                order_db.add_pay_success(pay_order.mch_id, pay_order.id, total_fee, trade_no, extend)
                async_job.notify_mch(pay_order.id)
        else:
            _LOGGER.info(
                'dezhunpay pay query error, trade_status:%s error_message:%s' % (status, data['retMsg']))
    else:
        _LOGGER.warn('dezhunpay data error, status_code: %s', response.status_code)
